"""
WS-Discovery协议实现
用于发现局域网内的ONVIF设备
"""

import socket
import uuid
import xml.etree.ElementTree as ET
import time
from datetime import datetime
from typing import List, Optional
from urllib.parse import urlparse

from ..models.device import DeviceInfo, DeviceStatus
from ..utils.logger import get_logger

logger = get_logger(__name__)


class WSDiscovery:
    """WS-Discovery协议实现类"""
    
    def __init__(self, multicast_address: str = "239.255.255.250", multicast_port: int = 3702):
        self.multicast_address = multicast_address
        self.multicast_port = multicast_port
        self.buffer_size = 4096
        
    def create_probe_message(self) -> bytes:
        """创建WS-Discovery Probe消息"""
        message_id = str(uuid.uuid4())
        
        probe_xml = f'''<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
               xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
               xmlns:wsd="http://schemas.xmlsoap.org/ws/2005/04/discovery"
               xmlns:tdn="http://www.onvif.org/ver10/network/wsdl">
    <soap:Header>
        <wsa:Action>http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</wsa:Action>
        <wsa:MessageID>urn:uuid:{message_id}</wsa:MessageID>
        <wsa:To>urn:schemas-xmlsoap-org:ws:2005:04:discovery</wsa:To>
    </soap:Header>
    <soap:Body>
        <wsd:Probe>
            <wsd:Types>tdn:NetworkVideoTransmitter</wsd:Types>
        </wsd:Probe>
    </soap:Body>
</soap:Envelope>'''
        
        return probe_xml.encode('utf-8')
    
    def parse_probe_match(self, data: bytes, source_ip: str) -> Optional[DeviceInfo]:
        """解析ProbeMatch响应"""
        try:
            # 解析XML响应
            root = ET.fromstring(data.decode('utf-8'))
            
            # 定义命名空间
            namespaces = {
                'soap': 'http://www.w3.org/2003/05/soap-envelope',
                'wsd': 'http://schemas.xmlsoap.org/ws/2005/04/discovery',
                'wsa': 'http://schemas.xmlsoap.org/ws/2004/08/addressing'
            }
            
            # 检查是否是ProbeMatch响应
            action_elem = root.find('.//wsa:Action', namespaces)
            if action_elem is None or 'ProbeMatches' not in action_elem.text:
                return None
            
            # 提取XAddrs（设备端点URL）
            xaddrs_elem = root.find('.//wsd:XAddrs', namespaces)
            if xaddrs_elem is None or not xaddrs_elem.text:
                logger.warning(f"设备 {source_ip} 响应中缺少XAddrs")
                return None
            
            endpoint_url = xaddrs_elem.text.strip()
            
            # 提取设备类型
            types_elem = root.find('.//wsd:Types', namespaces)
            device_types = types_elem.text if types_elem is not None else ""
            
            # 提取作用域（可能包含设备信息）
            scopes_elem = root.find('.//wsd:Scopes', namespaces)
            scopes = scopes_elem.text if scopes_elem is not None else ""
            
            # 生成设备ID
            device_id = f"onvif_{source_ip}_{int(time.time())}"
            
            # 从URL中提取端口
            parsed_url = urlparse(endpoint_url)
            port = parsed_url.port or 80
            
            # 创建设备信息对象
            device_info = DeviceInfo(
                device_id=device_id,
                ip_address=source_ip,
                endpoint_url=endpoint_url,
                port=port,
                status=DeviceStatus.UNKNOWN
            )
            
            # 尝试从scopes中提取更多信息
            if scopes:
                device_info = self._extract_device_info_from_scopes(device_info, scopes)
            
            logger.info(f"发现ONVIF设备: {source_ip} -> {endpoint_url}")
            return device_info
            
        except ET.ParseError as e:
            logger.error(f"解析XML响应失败: {e}")
            return None
        except Exception as e:
            logger.error(f"解析ProbeMatch响应时出错: {e}")
            return None
    
    def _extract_device_info_from_scopes(self, device_info: DeviceInfo, scopes: str) -> DeviceInfo:
        """从scopes中提取设备信息"""
        try:
            # scopes通常包含设备的基本信息，格式类似：
            # onvif://www.onvif.org/type/NetworkVideoTransmitter
            # onvif://www.onvif.org/hardware/manufacturer/value
            # onvif://www.onvif.org/name/value
            
            scope_parts = scopes.split()
            for scope in scope_parts:
                if 'manufacturer' in scope.lower():
                    # 尝试提取厂商信息
                    parts = scope.split('/')
                    if len(parts) > 0:
                        device_info.manufacturer = parts[-1]
                elif 'name' in scope.lower():
                    # 尝试提取设备名称
                    parts = scope.split('/')
                    if len(parts) > 0:
                        device_info.model = parts[-1]
                        
        except Exception as e:
            logger.warning(f"从scopes提取设备信息失败: {e}")
        
        return device_info
    
    def discover_devices(self, timeout: int = 10) -> List[DeviceInfo]:
        """发现ONVIF设备"""
        devices = []
        discovered_ips = set()  # 避免重复发现同一设备
        
        logger.info(f"开始WS-Discovery设备发现，超时时间: {timeout}秒")
        
        # 创建UDP套接字
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.settimeout(1.0)  # 设置较短的接收超时
        
        try:
            # 发送Probe消息
            probe_msg = self.create_probe_message()
            sock.sendto(probe_msg, (self.multicast_address, self.multicast_port))
            logger.info(f"发送WS-Discovery Probe消息到 {self.multicast_address}:{self.multicast_port}")
            
            # 接收响应
            start_time = time.time()
            while (time.time() - start_time) < timeout:
                try:
                    data, addr = sock.recvfrom(self.buffer_size)
                    source_ip = addr[0]
                    
                    # 避免重复处理同一IP的响应
                    if source_ip in discovered_ips:
                        continue
                    
                    device_info = self.parse_probe_match(data, source_ip)
                    if device_info:
                        devices.append(device_info)
                        discovered_ips.add(source_ip)
                        
                except socket.timeout:
                    # 超时是正常的，继续等待其他响应
                    continue
                except Exception as e:
                    logger.warning(f"接收响应时出错: {e}")
                    continue
                    
        except Exception as e:
            logger.error(f"发现设备时出错: {e}")
        finally:
            sock.close()
        
        logger.info(f"WS-Discovery完成，发现 {len(devices)} 个设备")
        return devices
    
    def probe_specific_device(self, target_ip: str, timeout: int = 5) -> Optional[DeviceInfo]:
        """探测指定IP的ONVIF设备"""
        logger.info(f"探测指定设备: {target_ip}")
        
        # 创建UDP套接字
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.settimeout(timeout)
        
        try:
            # 发送Probe消息到指定IP
            probe_msg = self.create_probe_message()
            sock.sendto(probe_msg, (target_ip, self.multicast_port))
            
            # 等待响应
            start_time = time.time()
            while (time.time() - start_time) < timeout:
                try:
                    data, addr = sock.recvfrom(self.buffer_size)
                    if addr[0] == target_ip:
                        device_info = self.parse_probe_match(data, target_ip)
                        if device_info:
                            logger.info(f"成功探测到设备: {target_ip}")
                            return device_info
                except socket.timeout:
                    break
                except Exception as e:
                    logger.warning(f"接收响应时出错: {e}")
                    break
                    
        except Exception as e:
            logger.error(f"探测设备 {target_ip} 时出错: {e}")
        finally:
            sock.close()
        
        logger.warning(f"未能探测到设备: {target_ip}")
        return None


class DiscoveryService:
    """设备发现服务"""
    
    def __init__(self, multicast_address: str = "239.255.255.250", multicast_port: int = 3702):
        self.ws_discovery = WSDiscovery(multicast_address, multicast_port)
        self.discovered_devices = {}  # device_id -> DeviceInfo
        
    def discover_all_devices(self, timeout: int = 10) -> List[DeviceInfo]:
        """发现所有设备"""
        devices = self.ws_discovery.discover_devices(timeout)
        
        # 更新设备列表
        for device in devices:
            self.discovered_devices[device.device_id] = device
            
        return devices
    
    def probe_device(self, target_ip: str, timeout: int = 5) -> Optional[DeviceInfo]:
        """探测指定设备"""
        device = self.ws_discovery.probe_specific_device(target_ip, timeout)
        
        if device:
            self.discovered_devices[device.device_id] = device
            
        return device
    
    def get_discovered_devices(self) -> List[DeviceInfo]:
        """获取已发现的设备列表"""
        return list(self.discovered_devices.values())
    
    def get_device_by_ip(self, ip_address: str) -> Optional[DeviceInfo]:
        """根据IP地址获取设备"""
        for device in self.discovered_devices.values():
            if device.ip_address == ip_address:
                return device
        return None
    
    def clear_discovered_devices(self):
        """清空已发现的设备"""
        self.discovered_devices.clear()
        logger.info("已清空发现的设备列表")
